#pragma once
/*
实现一个日志器模块--负责整合其它模块，提供接口给用户实现打印日志的功能
功能：1 管理把日志元素格式化的模块对象
2 管理日志落地模块对象
3 提供锁对象保证多线程下写入同一个文件安全

方法：
1提供一个抽象基类提供方法
2派生出一个同步日志器对象和异步日志器对象
*/

#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <thread>
#include <mutex>
#include <atomic>
#include <stdarg.h>
#include <stdlib.h>
#include "Unit.hpp"
#include "Level.hpp"
#include "Format.hpp"
#include "LogLand.hpp"
#include "UsyncLogger.hpp"
namespace MyLogger
{
    class Logger
    {
    public:
        Logger(const std::string &Logname, LogLevel::Level level, std::shared_ptr<Format::ForMatter> &formater, std::vector<std::shared_ptr<LogLand>> &LogLand)
            : _LoggerName(Logname), _Limit_Level(level), _formater(formater), _LogLand(LogLand.begin(), LogLand.end())
        {
        }
        // 提供方法给派生类实现---实现根据给定的格式化字符串实现不同等级的日志输按照指定格式出到指定方向
        // 可变参数为日志消息主体，构成消息主体不一定都是字符串类型的，还有int什么的，支持用户传入
        // 指定的格式和参数来构成消息主体
        void Debug(const std::string &file, size_t line, const std::string &fmt, ...)
        {
            // 通过传入的参数构造一个  struct LogMeg对象最按照用户给定格式字符串化终落地输出
            // 1 判断日志等级，如果小于用户给定的等级则该日志不输出
            if (LogLevel::Level::DEBUG < _Limit_Level)
                return;

            // 2解析不定参数来形成消息主体
            va_list ap;
            va_start(ap, fmt);
            char *res;
            int n = vasprintf(&res, fmt.c_str(), ap);
            if (n == -1)
            {
                std::cerr << "vasprintf falie" << std::endl;
                return;
            }
            va_end(ap);
            // 3 构造LogMeg对象 4 格式化LogMeg对象5 落地输出

           // std::cerr << "[" << __FILE__ << "]" << "[" << __LINE__ << "]" << '\n';

            MakeLogLand(LogLevel::Level::DEBUG, file, line, res);
        }
        void Infor(const std::string &file, size_t line, const std::string &fmt, ...)
        {

            if (LogLevel::Level::INFO < _Limit_Level)
                return;
            // 2解析不定参数来形成消息主体
            va_list ap;
            va_start(ap, fmt);
            char *res;
            int n = vasprintf(&res, fmt.c_str(), ap);
            if (n == -1)
            {
                std::cerr << "vasprintf falie" << std::endl;
                return;
            }
            va_end(ap);
            // 3 构造LogMeg对象 4 格式化LogMeg对象5 落地输出
            MakeLogLand(LogLevel::Level::INFO, file, line, res);
        }
        void Warning(const std::string &file, size_t line, const std::string &fmt, ...)
        {
            if (LogLevel::Level::WARNING < _Limit_Level)
                return;
            // 2解析不定参数来形成消息主体
            va_list ap;
            va_start(ap, fmt);
            char *res;
            int n = vasprintf(&res, fmt.c_str(), ap);
            if (n == -1)
            {
                std::cerr << "vasprintf falie" << std::endl;
                return;
            }
            va_end(ap);
            // 3 构造LogMeg对象 4 格式化LogMeg对象5 落地输出
            MakeLogLand(LogLevel::Level::WARNING, file, line, res);
        }
        void Error(const std::string &file, size_t line, const std::string &fmt, ...)
        {
            if (LogLevel::Level::ERROR < _Limit_Level)
                return;
            // 2解析不定参数来形成消息主体
            va_list ap;
            va_start(ap, fmt);
            char *res;
            int n = vasprintf(&res, fmt.c_str(), ap);
            if (n == -1)
            {
                std::cerr << "vasprintf falie" << std::endl;
                return;
            }
            va_end(ap);
            // 3 构造LogMeg对象 4 格式化LogMeg对象5 落地输出
            MakeLogLand(LogLevel::Level::ERROR, file, line, res);
        }
        void Fatal(const std::string &file, size_t line, const std::string &fmt, ...)
        {
            if (LogLevel::Level::FATAL < _Limit_Level)
                return;
            // 2解析不定参数来形成消息主体
            va_list ap;
            va_start(ap, fmt);
            char *res;
            int n = vasprintf(&res, fmt.c_str(), ap);
            if (n == -1)
            {
                std::cerr << "vasprintf falie" << std::endl;
                return;
            }
            va_end(ap);
            // 3 构造LogMeg对象 4 格式化LogMeg对象5 落地输出
            MakeLogLand(LogLevel::Level::FATAL, file, line, res);
        }

    public:
        std::string Getname()
        {
            return _LoggerName;
        }
        // 提供一个具体的抽象接口实现不同的日志的输出方向
        virtual void WriteLog(const char *data, size_t len) = 0;
        void MakeLogLand(LogLevel::Level level, const std::string &file, const size_t line, char *res)
        {
            // 3 构造LogMeg对象
            LogMessage::LogMeg Lmsg(level, line, file, std::string(res), _LoggerName);
            free(res);
            // 4 格式化LogMeg对象
            std::stringstream ss;
            _formater->format(ss, Lmsg);
            // 5 落地输出
            WriteLog(ss.str().c_str(), ss.str().length());
        }

    protected:
        std::mutex _mutex;                              // 锁保证写入安全
        std::string _LoggerName;                        // 日志器名称
        std::atomic<LogLevel::Level> _Limit_Level;      // 定义一个原子性的日志等级类的对象，来限制日志的输出
        std::shared_ptr<Format::ForMatter> _formater;   // 把用户的指定格式解析然后返回指定格式的日志
        std::vector<std::shared_ptr<LogLand>> _LogLand; // 日志的输出方向--stdout,普通文件，滚动文件,一个日志器对象可能有多个落地方向
    };

    // 派生一个同步日志器对象直接把日志输出到指定方向去
    // 定义一个派生类时首先会定义一个基类出来也就是说派生类的构造函数一定要先初始化继承的基类对象
    class SynLogger : public MyLogger::Logger
    {
    public:
        SynLogger(const std::string &Logname, LogLevel::Level level,
                  std::shared_ptr<Format::ForMatter> &formater, std::vector<std::shared_ptr<LogLand>> &LogLand)
            : Logger(Logname, level, formater, LogLand) // 构造继承的基类，初始化列表会调用继承的对象或者成员变量的合适的构造函数
        {
        }

    public:
        void WriteLog(const char *data, size_t len) override
        {
            // 智能锁RAII风格，定义时加锁，销毁时自动解锁
            // 这种写法比直接使用mutex.lock()/unlock()更安全，能避免忘记解锁导致的死锁问题。
            std::unique_lock<std::mutex> _lock(_mutex);
            // 如果没有落地对象那么返回
            if (_LogLand.empty())
                return;
            // 调用不同的落地对象把日志落地到不同的方向
            for (auto &LgLd : _LogLand)
            {
                LgLd->WriteLog(data, len);
            }
        }
    };

    // 派生一个异步日志器对象直接把日志输出到指定方向去
    // 定义一个派生类时首先会定义一个基类出来也就是说派生类的构造函数一定要先初始化继承的基类对象
    class USynLogger : public MyLogger::Logger
    {
    public:
        USynLogger(const std::string &Logname, LogLevel::Level level,
                   std::shared_ptr<Format::ForMatter> &formater, std::vector<std::shared_ptr<LogLand>> &LogLand)
            : Logger(Logname, level, formater, LogLand) // 构造继承的基类，初始化列表会调用继承的对象或者成员变量的合适的构造函数
              ,
              _Usync(std::make_shared<UsyncLogger>([this](LogBuffer &buffer)
                                                   { Reallog(buffer); }))
        {
        }

    public:
        void WriteLog(const char *data, size_t len) override
        {
            std::string me(data, len);
            // 往生产者放入如数据
            _Usync->push(me);
        }
        // 设计回调函数处理日志落地
        void Reallog(LogBuffer &buffer)
        {

            // 如果没有落地对象那么返回
            if (_LogLand.empty())
                return;
            // 调用不同的落地对象把日志落地到不同的方向
            int nums = buffer.GetRMesNum();
            // std::cerr << "[" << __FILE__ << "]" << "[" << __LINE__ << "]" << '\n';
            // std::cout<<nums<<std::endl;
            for (int i = 0; i < nums; i++)
            {
                for (auto &LgLd : _LogLand)
                {
                    // std::cerr << "[" << __FILE__ << "]" << "[" << __LINE__ << "]" << buffer.GetCurRPos()<<'\n';

                    LgLd->WriteLog(buffer.GetCurRPos().c_str(), buffer.GetCurRPos().length());
                }
                buffer.ReadPosOffSet();
            }
        }

    private:
        // 一个异步管理器里面管理这个两个缓冲区，负责创建一个线程处理日志
        std::shared_ptr<UsyncLogger> _Usync; // 管理一个异步管理器负责创建线程处理日志
    };

    // 使用建造者模式去构造一个日志器对象，而不是让用户去创建日志器的各个模块然后再去创建日志器对象
    // 1 提供一个基类对象提供构造各个模块的方法
    // 2 派生具体的子类来建造全局的日志器对象和局部的对象

    // 定义一个日志器枚举类
    enum class LoggerType
    {
        LOGGER_SYNC, // 同步
        LOGGER_USYNC // 异步
    };
    class LoggerBuilder
    {
    public:
        // 等级和日志类信息先给默认值
        LoggerBuilder()
            : _Limit_Level(LogLevel::Level::DEBUG), _LogType(LoggerType::LOGGER_SYNC)
        {
        }
        // 提供构造日志器每一个模块的方法
        void BuildLoggerType(LoggerType LogType) { _LogType = LogType; }
        void BuildLoggerName(const std::string &LogName) { _LoggerName = LogName; }
        void BuildLogger_Limit_Level(LogLevel::Level Le) { _Limit_Level = Le; }
        void BuildLoggerFormater(const std::string &formatstr = "[%d][%t][%c][%f][%l][%p]{%m}%T\n")
        {
            _formater = std::make_shared<Format::ForMatter>(formatstr);
        }
        // 根据不同的落地类型实现不同的落地对象
        template <class LandType, class... Args>
        void BuiltLogLand(Args &&...args)
        {
            // 参数包类型传递给下一层需要展开参数包 Args...
            std::shared_ptr<LogLand> LogLand = LandFactory<LandType, Args...>::CreateLogLand(std::forward<Args>(args)...);
            _LogLand.push_back(LogLand);
        }

        // 构建一个日志器对象
        virtual std::shared_ptr<MyLogger::Logger> Build() = 0;

    protected:
        std::mutex _mutex;       // 锁保证写入安全
        std::string _LoggerName; // 日志器名称
        LoggerType _LogType;
        std::atomic<LogLevel::Level> _Limit_Level;      // 定义一个原子性的日志等级类的对象，来限制日志的输出
        std::shared_ptr<Format::ForMatter> _formater;   // 把用户的指定格式解析然后返回指定格式的日志
        std::vector<std::shared_ptr<LogLand>> _LogLand; // 日志的输出方向--stdout,普通文件，滚动文件,一个日志器对象可能有多个落地方向
    };

    // 派生一个生成局部日志器类
    class BuildLocalLogger : public LoggerBuilder
    {
    public:
        BuildLocalLogger(const std::string &name)
        {
            _LoggerName = name;
        }
        std::shared_ptr<MyLogger::Logger> Build() override
        {
            // 日志器名称必须有
            assert(!_LoggerName.empty());
            if (_formater.get() == nullptr) // 如果格式化对象为空则构建一个
            {
                BuildLoggerFormater();
            }
            if (_LogLand.empty()) // 如果落地方向为空
            {
                BuiltLogLand<LogToStdOut>();
            }
            if (_LogType == LoggerType::LOGGER_USYNC)
                return std::make_shared<USynLogger>(_LoggerName, _Limit_Level, _formater, _LogLand);

            return std::make_shared<SynLogger>(_LoggerName, _Limit_Level, _formater, _LogLand);
        }
    };

    class LoggerManager
    {
    public:
        static LoggerManager &GerInsrance()
        {
            static LoggerManager Single;
            return Single;
        }

        // 添加日志器对象,线程安全的添加
        void AddLogger(std::shared_ptr<MyLogger::Logger> &Logger)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            if (!_Loggers.count(Logger->Getname())) // 如果日志器已经存在则不添加
                _Loggers.insert({Logger->Getname(), Logger});
        }

        // 获取日志器对象，返回智能指针对象的拷贝实际上就是返回new出来的指针
        std::shared_ptr<MyLogger::Logger> GetLogger(const std::string &name)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            std::cout<<name<<std::endl;
            if (_Loggers.count(name)) // 如果日志器已经存在则放回
                return _Loggers[name];
            std::cout<<name<<std::endl;
            return _Df_Loger; // 不存在返回默认的
        }
        // 获取默认日志器对象
        std::shared_ptr<MyLogger::Logger> GetDfLogger()
        {
            std::unique_lock<std::mutex> lock(_mutex);
            return _Df_Loger; // 返回默认的
        }

    private:
        LoggerManager()
        {
            std::unique_ptr<MyLogger::LoggerBuilder> Buile = std::make_unique<MyLogger::BuildLocalLogger>("Root");
            _Df_Loger = Buile->Build(); // 建造一个默认的日志器
        }

    private:
        std::mutex _mutex;                                                           // 多个线程操作日志器接口得加锁
        std::shared_ptr<MyLogger::Logger> _Df_Loger;                                 // 默认的日志器
        std::unordered_map<std::string, std::shared_ptr<MyLogger::Logger>> _Loggers; // 管理不同的日志器对象
    };

    class BuildGlobalLogger : public LoggerBuilder
    {
    public:
        BuildGlobalLogger(const std::string &name)
        {
            _LoggerName = name;
        }
        std::shared_ptr<MyLogger::Logger> Build() override
        {
            // 日志器名称必须有
            assert(!_LoggerName.empty());
            if (_formater.get() == nullptr) // 如果格式化对象为空则构建一个
            {
                BuildLoggerFormater();
            }
            if (_LogLand.empty()) // 如果落地方向为空
            {
                //std::cout<<"111"<<std::endl;
                BuiltLogLand<LogToStdOut>();
            }
            std::shared_ptr<MyLogger::Logger> logger;
            if (_LogType == LoggerType::LOGGER_USYNC)
                logger = std::make_shared<USynLogger>(_LoggerName, _Limit_Level, _formater, _LogLand);
            else
                logger = std::make_shared<SynLogger>(_LoggerName, _Limit_Level, _formater, _LogLand);
            // 直接把获取到的日志器添加到管理对象中去
            LoggerManager::GerInsrance().AddLogger(logger);
            return logger;
        }
    };
};